/*
 * Terminador.cpp
 *
 *  Created on: 09/10/2012
 */

#include "Terminador.h"

/**
 * Constructor del terminador.
 * Recibe el tiempo que debe durar la simulacion.
 * Registra los handlers de señales.
 */
Terminador::Terminador(int tiempo, vector<Pipe>* pipesSalidas)
	: tiempo (tiempo),
	  pidsEntradas (vector<pid_t>()),
	  pidsSalidas (vector<pid_t>()),
	  pipesSalidas (pipesSalidas){

	SignalHandler::getInstance()->registrarHandler(SIGINT, this);
	SignalHandler::getInstance()->registrarHandler(SIGTERM, this);

	Log::getInstance()->getInstance()->loguear("Terminador: creado.");
}

/**
 * Destructor del terminador.
 * Elimina los handlers de señales.
 */
Terminador::~Terminador() {
	SignalHandler::getInstance()->removerHandler(SIGINT);
	SignalHandler::getInstance()->removerHandler(SIGTERM);
	SignalHandler::destruir();
}

void Terminador:: inicializar(){
	for (vector<Pipe>::iterator it = pipesSalidas->begin(); it != pipesSalidas->end(); it++){
		it->setearModo(ESCRITURA);
	}
}

void Terminador::cerrarPipes(){
	Log::getInstance()->loguear("Terminador: se cierran los pipes de las salidas.");

	for (vector<Pipe>::iterator it = pipesSalidas->begin(); it != pipesSalidas->end(); it++){
		it->cerrar();
	}
}

/**
 * Handler de señales:
 * SIGTERM: Señal que el usuario envia desde fuera de la aplicacion.
 * 		El terminador comienza la finalizacion de la ejecucion ordenadamente.
 * SIGINT: Señal que el usuario envia desde fuera de la aplicacion.
 * 		El terminador comienza la finalizacion de la ejecucion ordenadamente.
 */
int Terminador::handleSignal( int signum ){
	if (signum == SIGTERM){
		Log::getInstance()->loguear("Terminador: me llego SIGTERM. Voy a finalizar.");

	} else if (signum == SIGINT) {
		Log::getInstance()->loguear("Terminador: me llego SIGINT. Voy a finalizar.");

	}
	return 0;
}

/**
 * Metodo que se ejecuta mientras quede tiempo de simulacion o no hayan
 * llegado notificaciones de error desde los procesos hijos.
 */
void Terminador::ejecutar(){
	Log::getInstance()->loguear("Terminador: me duermo " + intToString(this->tiempo) + " segundos.");
	int tiempoRestante = sleep(this->tiempo);
	if (tiempoRestante == 0){
		Log::getInstance()->imprimir("Terminador: se terminó el tiempo de simulación.");
	} else {
		Log::getInstance()->imprimir("Terminador: el usuario detiene la simulación.");
	}
	this->terminar();
}

/**
 * Metodo que envia seniales SIGUSR1 a todas las entradas y las salidas para
 * que finalicen la ejecución.
 *
 * Espera a que se mueran todos ya que el proceso del terminador es quien
 * se encarga de borrar los semáforos.
 */
void Terminador::terminar(){
	Log::getInstance()->loguear("Terminador: comienzo el envio de señales para finalizar el programa.");
	int state;

	// Se matan las entradas
	for (vector<pid_t>::iterator itEntradas = this->pidsEntradas.begin();  itEntradas != this->pidsEntradas.end() ; itEntradas++){
		kill(*itEntradas, SIGUSR1);
		waitpid(*itEntradas, &state, 0);
	}

	// Se cierran los pipes de las salidas.
	this->cerrarPipes();

	// Espera a que mueran las salidas.
	for (vector<pid_t>::iterator itSalidas = this->pidsSalidas.begin(); itSalidas != this->pidsSalidas.end() ; itSalidas++){
		waitpid(*itSalidas, &state, 0);
	}
	
	// Se mata el proceso de consulta.
	kill(pidConsulta, SIGUSR1);
	waitpid(pidConsulta, &state, 0);
}

void Terminador::agregarPidEntrada(pid_t pidEntrada){
	this->pidsEntradas.push_back(pidEntrada);
}

void Terminador::setPidConsulta(pid_t pid){
	this->pidConsulta = pid;
}

void Terminador::agregarPidSalida(pid_t pidSalida){
	this->pidsSalidas.push_back(pidSalida);
}
